﻿using System;
using System.Collections.Generic;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using RAP.Framework.Libary.Utils;

namespace RAP.Framework.Libary.Payment.WxPay
{
  /// <summary>
  /// 支付API实现
  /// </summary>
  public class PayAPI
  {
    public const string RETURN_SUCCESS = "SUCCESS";

    public PayConfig PayConfig { get; set; }

    public PayAPI(PayConfig payConfig)
    {
      PayConfig = payConfig;
    }

    /// <summary>
    /// 检测签名是否正确
    /// </summary>
    /// <returns></returns>
    public bool CheckSign(PayOrderRequestData orderData)
    {
      var sign = orderData.Sign;
      if (sign.IsNullOrEmpty())
      {
        return false;
      }
      var localSign = GenerateSign(orderData, orderData.AppKey);
      if (sign == localSign)
      {
        return true;
      }
      throw new PayException("签名验证失败！");
    }

    /// <summary>
    /// 生成签名，详见签名生成算法
    /// </summary>
    /// <returns></returns>
    public string GenerateSign(PayDataBase orderData, string appKey)
    {
      //转url格式
      string str = orderData.ToUrlParams();
      //在string后加入API KEY
      str += "&key=" + appKey;
      var bs = MD5.Create().ComputeHash(Encoding.UTF8.GetBytes(str));
      var sb = new StringBuilder();
      foreach (var b in bs)
      {
        sb.Append(b.ToString("X2"));
      }
      return sb.ToString();
    }

    /// <summary>
    /// 生成沙箱模式下的签名
    /// </summary>
    /// <param name="orderData"></param>
    /// <returns></returns>
    public async Task<string> GenerateSandboxSign(PayOrderRequestData orderData)
    {
      orderData.Sign = GenerateSign(orderData, orderData.AppKey);

      var resXml = await PayConfig.GetSignKey.SubmitAsync(orderData.ToXML(), "text/xml");
      if (resXml.Code == HttpStatusCode.OK)
      {
        var resultData = PayDataBase.ParseXML<PaySandboxOrderResultData>(resXml.Data);
        if (resultData.ReturnCode == RETURN_SUCCESS)
        {
          return resultData.Sign;//sandbox_signkey
        }
      }
      return string.Empty;
    }

    /// <summary>
    /// 生成订单号
    /// </summary>
    /// <returns></returns>
    public string GenerateOutTradeNo(object id)
    {
      return $"{DateTime.Now.ToString("yyyyMMddHHmmss")}{new Random().Next(9999).ToString("0000")}{id}";
    }

    /// <summary>
    /// 生成盐字符串
    /// </summary>
    /// <returns></returns>
    public string GenerateSaltString()
    {
      return Guid.NewGuid().ToString().MD5Encrypt(false);
    }

    /**
    * 生成时间戳，标准北京时间，时区为东八区，自1970年1月1日 0点0分0秒以来的秒数
     * @return 时间戳
    */
    public string GenerateTimeStamp()
    {
      var ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
      return ts.TotalSeconds.ToString();
    }

    /// <summary>
    /// 验证签名是否正确
    /// </summary>
    /// <param name="data"></param>
    /// <returns></returns>
    public bool VerifySign(PayDataBase data)
    {
      var sign = GenerateSign(data, PayConfig.AppKey);
      return sign == data.Sign;
    }

    public async Task<PayOrderResultData> UnifiedOrder(PayOrderRequestData payData, int timeout = 3000)
    {
      if (payData.TradeNo.IsNullOrEmpty())
      {
        throw new PayException("缺少统一支付接口必填参数TradeNo！");
      }
      else if (payData.Body.IsNullOrEmpty())
      {
        throw new PayException("缺少统一支付接口必填参数Body！");
      }
      else if (payData.TotalFee <= 0)
      {
        throw new PayException("缺少统一支付接口必填参数TotalFee！");
      }
      else if (payData.NotifyUrl.IsNullOrEmpty())
      {
        payData.NotifyUrl = this.PayConfig.NotifyUrl;
      }
      payData.MarketId = this.PayConfig.MarketId;
      payData.Salt = GenerateSaltString();
      if (PayConfig.GetSignKey.IsNotNullOrEmpty())
      {
        //获取沙盒模式下的Sign
        var sandboxPayData = new PayOrderRequestData
        {
          MarketId = payData.MarketId,
          Salt = payData.Salt
        };
        payData.TotalFee = 301;
        payData.AppKey = await GenerateSandboxSign(sandboxPayData);
      }
      else
      {
        payData.AppKey = PayConfig.AppKey;
      }
      payData.AppId = this.PayConfig.AppId;
      payData.FeeType = "CNY";
      payData.TradeType = "APP";
      payData.Sign = GenerateSign(payData, payData.AppKey);
      var xmlData = payData.ToXML();
      var start = DateTime.Now;
      try
      {
        var result = await this.PayConfig.OrderUrl.SubmitAsync(xmlData, "text/xml", timeout);
        if (result.Code == HttpStatusCode.OK)
        {
          return PayDataBase.ParseXML<PayOrderResultData>(result.Data);
        }
        else
        {
          throw new PayException(result.Data);
        }
      }
      catch (ThreadAbortException e)
      {
        Thread.ResetAbort();
        throw new PayException(e.Message);
      }
      catch (WebException e)
      {
        throw new PayException($"StatusCode : {((HttpWebResponse)e.Response).StatusCode}\r\n\tStatusDescription :{((HttpWebResponse)e.Response).StatusDescription}");
      }
      catch (Exception e)
      {
        throw new PayException(e.ToString());
      }
    }

    /// <summary>
    /// 将订单返回数据包装成客户端微信支付所需数据结构
    /// </summary>
    /// <param name="orderResultData"></param>
    /// <returns></returns>
    public PayOrderClientAppData GenerateClientAppData(PayOrderResultData orderResultData)
    {
      //根据返回数据按DCloud的格式要求再次签名并返回
      var clientAppData = new PayOrderClientAppData
      {
        AppId = orderResultData.AppId,
        MarketId = orderResultData.MarketId,
        PrePayId = orderResultData.PrePayId,
        Package = "Sign=WXPay",
        Salt = GenerateSaltString(),
        Timestamp = GenerateTimeStamp()
      };
      clientAppData.Sign = GenerateSign(clientAppData, PayConfig.AppKey);
      return clientAppData;
    }
  }
}
